﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using UmlControlLibrary;

namespace UmlControlLibrary
{
    public class UmlDiagram:UmlElement, IEventListener
    {
        public UmlDiagram():base()
        {
        /*    UmlPackage systemPackage = new UmlPackage();
            systemPackage.AddElement(new UmlType("String"));
            systemPackage.AddElement(new UmlType("Int"));
            systemPackage.AddElement(new UmlType("Bool"));
            Packages.Add("System", systemPackage);
            */
            this.UmlElementAdded += new UmlElementAddedDelegate(UmlDiagram_UmlElementAdded);

            EventService.RegisterListener(typeof(UmlProperty), this, true);
            EventService.RegisterListener(typeof(UmlElement), this, true);
        }

        private Dictionary<UmlElement, UmlBaseControl> _diagramControls = new Dictionary<UmlElement, UmlBaseControl>();
        public Dictionary<UmlElement, UmlBaseControl> DiagramControls
        {
            get { return _diagramControls;}
        }

        private Dictionary<string, UmlPackage> _packages = new Dictionary<string, UmlPackage>();
        public Dictionary<string, UmlPackage> Packages
        {
            get { return _packages; }
        }

        public void AddElementControl(UmlBaseControl control)
        {
            if (control.DataContext is UmlElement)
                DiagramControls.Add((UmlElement)control.DataContext, control);
        }

        /*public void CheckElement(UmlElement element)
        {
            if (element is UmlProperty)
            {
                UmlElement type = null;//UmlElementList.Instance.GetElementByName(((UmlProperty)element).TypeName);
                if (type == null)
                {
                    int i = 0;
                    string typename = ((UmlProperty)element).TypeName;
                    foreach (UmlPackage package in Packages.Values)
                    {
                        if (package.Contains(typename))
                        {
                            type = package.GetElement(typename);
                            i++;
                        }
                    }
                    if (i > 1)
                        throw new Exception("Ambiguous type name, found in packages yada yada");

                }
                if (type != null)
                    AddElement(new UmlRelation(element, type, UmlRelationType.Association));
            }

        }
        */

        void UmlDiagram_UmlElementAdded(UmlElementEventArgs args)
        {
            if (args.Sender is UmlRelation)
            {
                // TODO : intercept and change name if it already exists.
                // But check only in the end1 owner class if end1 is an UmlProperty
            }
            else
            {
                if (GetElementByGuid(args.Sender.Guid) != null)
                    args.Cancel = true;
            }
        }

        #region IEventListener Members

        public void HandleEvent(EventServiceArgs args)
        {
            if (args is EventServiceValidationArgs)
            {
                //if (((EventServiceValidationArgs)args).Event
            }
            else
            {
                if (args.Sender is UmlProperty)
                {
                    if ((args.Event == UmlProperty.Events.Created)
                        || (args.Event == UmlProperty.Events.Modified)
                        || (args.Event == UmlProperty.Events.CardinalityModified))
                    {
                        UmlProperty p = (UmlProperty)args.Sender;
                        UmlElement target = this.GetElementByName(p.TypeName);
                        if (target != null)
                        {
                            IEnumerable<UmlElement> relations = (this.GetRelationsFor(p));
                            EventService.DelayBroadcasting();

                            UmlRelation relation = (UmlRelation)relations.Where(r => ((r as UmlRelation).End1.EndElement == target) || ((r as UmlRelation).End2.EndElement == target)).FirstOrDefault();
                            if (relation == null)
                            {
                                relation = new UmlRelation(p, target, UmlRelationType.Association);
                                this.AddElement(relation);
                            }

                            if (relation != null)
                            {
                                string card = "1";
                                UmlRelationType rType = relation.RelationType;
                                if (p.ArrayKind != null)
                                {
                                    switch (p.ArrayKind)
                                    {
                                        case UmlProperty.ArrayType.Array:
                                            card = (p.ArraySize == null ? "*" : p.ArraySize.ToString());
                                            rType = UmlRelationType.Composition;
                                            break;
                                        case UmlProperty.ArrayType.List:
                                            card = "*";
                                            rType = UmlRelationType.Composition;
                                            break;
                                    }
                                }
                                relation.End2.Label = card;
                                relation.RelationType = rType;
                            }
                            //new UmlRelation(p, target, UmlRelationType.Association);
                            EventService.ResumeBroadcasting();
                        }
                    }
                }
                else if (args.Sender is DiagramElement)
                {
                    if (args.Event == DiagramElement.Events.InheritanceModified)
                    {
                        DiagramElement p = (DiagramElement)args.Sender;
                        IEnumerable<UmlElement> relations = (this.GetRelationsFor(p));
                        EventService.DelayBroadcasting();

                        IEnumerable<UmlRelation> inheritanceRelations = relations.Where(r => (r as UmlRelation).RelationType == UmlRelationType.Inheritance).Cast<UmlRelation>();

                        string[] parents = p.Parent.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                        for (int i = 0; i < parents.Length; i++)
                            parents[i] = parents[i].Trim();

                        UmlRelation[] deletableRelations = inheritanceRelations.Where(r => !parents.Contains(r.End2.EndElement.Name)).ToArray();
                        foreach (UmlRelation r in deletableRelations)
                        {
                            // destroy relation
                        }

                        foreach (string parent in parents)
                        {
                            UmlRelation relation = inheritanceRelations.Where(r => r.End2.EndElement.Name == parent).FirstOrDefault();
                            if (relation == null)
                            {
                                UmlElement target = this.GetElementByName(parent);
                                if (target != null)
                                {
                                    relation = new UmlRelation(p, target, UmlRelationType.Inheritance);
                                    this.AddElement(relation);
                                }
                            }
                        }
                        //new UmlRelation(p, target, UmlRelationType.Association);
                        EventService.ResumeBroadcasting();
                    }
                    else if (args.Event == UmlNote.Events.LinksModified)
                    {
                        UmlNote p = (UmlNote)args.Sender;
                        IEnumerable<UmlElement> relations = (this.GetRelationsFor(p));
                        EventService.DelayBroadcasting();

                        IEnumerable<UmlRelation> noteRelations = relations.Where(r => (r as UmlRelation).RelationType == UmlRelationType.Note).Cast<UmlRelation>();

                        UmlRelation[] deletableRelations = noteRelations.Where(r => !p.AnnotedElements.Contains(r.End2.EndElement.Name)).ToArray();
                        foreach (UmlRelation r in deletableRelations)
                        {
                            // destroy relation
                        }

                        foreach (string linkName in p.AnnotedElements)
                        {
                            UmlRelation relation = noteRelations.Where(r => r.End2.EndElement.Name == linkName).FirstOrDefault();
                            if (relation == null)
                            {
                                UmlElement target = this.GetElementByName(linkName);
                                if (target != null)
                                {
                                    relation = new UmlRelation(p, target, UmlRelationType.Note);
                                    this.AddElement(relation);
                                }
                            }
                        }
                        //new UmlRelation(p, target, UmlRelationType.Association);
                        EventService.ResumeBroadcasting();
                    }
                }
            }
        }

        public override UmlElement GetElementByGuid(string guid)
        {
            return DiagramControls.Keys.FirstOrDefault(e => e.Guid == guid);
        }

        public override UmlElement GetElementByName(string name)
        {
            return DiagramControls.Keys.FirstOrDefault(e => e.Name == name);
        }
        #endregion
    }
}
